#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include "interface.h"

int maj_no;
int mi_no = 0;
char devname[] = "adc_dev";
const int count = 1;
int number;
const int baseminor = 0;
struct cdev *pdev;
struct class *pclass;
unsigned int *pcon=NULL;
unsigned int *pcon_1=NULL;
unsigned int *pdata = NULL;


int adc_dev_Open( struct inode *p,  struct file *f)
{
	//printk("adc_dev_ device open\n");
	return 0;
}

int adc_dev_Close( struct inode *p,  struct file *f)
{
	//printk("adc_dev_ device close\n");
	return 0;
}
long adc_dev_Ioctl(struct file *f, unsigned int cmd,  unsigned long arg)
{
	unsigned int tmp;
//	int __user *argp=(int __user *)arg;
		//printk("cmd ..........=%u\n",cmd);
	switch(cmd)
	{
		case ADC_L:			

			tmp=ioread32(pcon);
			tmp=tmp | 1;
			iowrite32(tmp,pcon);
			while(1)
			{
				if(ioread32(pcon) & (1<<15))
				{
	
					tmp=ioread32(pdata);
					tmp=tmp& 0x3ff;
					copy_to_user((int __user *)arg,&tmp,4);
					break;
				}
			}
	}
	return 0;
}
struct file_operations adc_dev_OPS =
{
	.owner = THIS_MODULE,
	.open = adc_dev_Open,
	.release = adc_dev_Close,
	.unlocked_ioctl = adc_dev_Ioctl,
};

int adc_dev_DriverProbe(struct platform_device *pDevice)
{
	struct resource *res = pDevice->resource;
	int i;
	struct device *pdevice;
	unsigned int tmp;

	pcon   = ioremap(res[0].start,4);
	pcon_1 =ioremap(res[2].start,4);			
	pdata  = ioremap(res[1].start,4);
	tmp = ioread32(pcon);
	tmp = tmp & ~(1 << 16);
	tmp = tmp | (1<<14);
	tmp = tmp & ~(0xff<<6);
	tmp = tmp | (19<<6);
	tmp = tmp & ~(1<<2);
	iowrite32(tmp, pcon);
	iowrite32(3,pcon_1);
	
	if(alloc_chrdev_region(&number,baseminor,count, devname))
	{
		printk("number register faild\n");
		return -1;
	}	 
	maj_no = MAJOR(number);
	pdev = cdev_alloc();
	if( NULL == pdev)
		goto CDEV_ALLOC_ERR;
	cdev_init(pdev, &adc_dev_OPS);
	if(cdev_add(pdev, number,count))
		goto CDEV_ADD_ERR;
	pclass = class_create(THIS_MODULE,"adc_dev_");
	if(IS_ERR(pclass))
		goto CLASS_CREATE_ERR;
	for(i = 0; i < count; i++)
	{
		pdevice = device_create(pclass, 0,MKDEV(maj_no,i),0, "adc_dev_%d",i);
		if(IS_ERR(pdevice))
			goto DEVICE_CREATE_ERR;	

	}
        printk("adc_dev_DriverProbe\r\n");
	
	return 0;
DEVICE_CREATE_ERR:
	class_destroy(pclass);
CLASS_CREATE_ERR:
CDEV_ADD_ERR:
	cdev_del(pdev);
CDEV_ALLOC_ERR:
	unregister_chrdev_region(number, count);
	return -1;	
}

int adc_dev_DriverRemove(struct platform_device *pDevice)
{
	int i;
	iounmap(pcon);
	iounmap(pdata);
	for(i = 0; i < count; i++)
		device_destroy(pclass, MKDEV(maj_no, i));
	class_destroy(pclass);
	cdev_del(pdev);
	unregister_chrdev_region(number,count);
	printk("exit module, cleanup_module\n");

        printk("adc_dev_DriverRemove\r\n");
        return 0;
}
static const struct of_device_id  adc_dev__of_match[] = {
	{.compatible = "fs4412,adc_dev"},
	{}
};

static struct platform_driver g_stPlantFormDriver =
{
        .driver = {
                .name = "adc_dev_plat",
                .owner = THIS_MODULE,
		.of_match_table = adc_dev__of_match,
        },
        .probe  = adc_dev_DriverProbe,
        .remove =  adc_dev_DriverRemove,
};

int adc_dev_Init(void)
{
        int iRet = 0;
        printk("driver,  module init enter\r\n");

        iRet = platform_driver_register(&g_stPlantFormDriver);
        if (0 != iRet)
        {
                printk("platform_driver_register error");
		return -1;
        }
        printk("driver,  module init exit\r\n");
        return iRet;

}

void adc_dev_Release(void)
{
        printk("driver,  clean module enter\r\n");
        platform_driver_unregister(&g_stPlantFormDriver);
        printk("driver,   clean module exit\r\n");
}

module_exit(adc_dev_Release);
module_init(adc_dev_Init);

MODULE_LICENSE("GPL");
